<?php
/**
 * 缓存基类
 * @author Tongle Xu <xutongle@gmail.com> 2013-9-14
 * @copyright Copyright (c) 2003-2103 tintsoft.com
 * @license http://www.tintsoft.com
 * @version $Id$
 */
class Cache {
	
	/**
	 * 标志存储时间
	 *
	 * @var string
	 */
	const STORETIME = 'store';
	
	/**
	 * 标志存储数据
	 *
	 * @var string
	 */
	const DATA = 'data';
	
	/**
	 * 配置文件中标志过期时间名称定义(也包含缓存元数据中过期时间的定义)
	 *
	 * @var string
	 */
	const EXPIRE = 'expires';
	
	/**
	 * 初始化
	 */
	public function init() {
	}
	
	/**
	 * 执行设置操作
	 *
	 * @param string $key 缓存数据的唯一key
	 * @param string $value 缓存数据值，该值是一个含有有效数据的序列化的字符串
	 * @param int $expires 缓存数据保存的有效时间，单位为秒，默认时间为0即永不过期
	 * @return boolean
	 * @throws Base_Exception 缓存失败的时候抛出异常
	 */
	protected function setValue($key, $value, $expire) {
		throw new Base_Exception ( get_class ( $this ) . ' does not support set() functionality.' );
	}
	
	/**
	 * 添加缓存
	 *
	 * @param unknown $key Key
	 * @param unknown $value 值
	 * @param unknown $expire 过期时间
	 * @throws Base_Exception
	 */
	protected function addValue($key, $value, $expire) {
		throw new Base_Exception ( get_class ( $this ) . ' does not support add() functionality.' );
	}
	
	/**
	 * 执行获取操作
	 *
	 * @param string $key 缓存数据的唯一key
	 * @return string 缓存的数据
	 * @throws Base_Exception 缓存数据获取失败抛出异常
	 */
	protected function getValue($key) {
		throw new Base_Exception ( get_class ( $this ) . ' does not support get() functionality.' );
	}
	
	/**
	 * 批量获取缓存信息
	 * 
	 * @param array $keys
	 * @return multitype:string
	 */
	protected function getValues($keys) {
		$results = array ();
		foreach ( $keys as $key )
			$results [$key] = $this->getValue ( $key );
		return $results;
	}
	
	/**
	 * 执行删除操作
	 *
	 * @param string $key 需要删除的缓存数据的key
	 * @return boolean
	 */
	protected function deleteValue($key) {
		throw new Base_Exception ( get_class ( $this ) . ' does not support delete() functionality.' );
	}
	
	/**
	 * 清空缓存
	 *
	 * @throws Exception
	 */
	protected function flushValues() {
		throw new Base_Exception ( get_class ( $this ) . ' does not support flushValues() functionality.' );
	}
	
	/**
	 * 根据缓存key获取指定缓存
	 *
	 * @param string $key 获取缓存数据的标识
	 * @return mixed 返回被缓存的数据
	 * @throws cache_exception 获取失败时抛出异常
	 */
	public function get($key) {
		$value = $this->getValue ( $key );
		N ( 'cache_read', 1 );
		return $this->formatData ( $key, $value );
	}
	
	/**
	 * 设置缓存
	 * 如果key不存在，添加缓存；否则，将会替换已有key的缓存。
	 *
	 * @param string $key 保存缓存数据的键。
	 * @param string $value 保存缓存数据。
	 * @param int $expires 缓存数据的过期时间,0表示永不过期
	 * @return boolean
	 * @throws Base_Exception 缓存失败时抛出异常
	 */
	public function set($key, $value, $expire = 0) {
		$value = $this->buildData ( $value, $expire );
		N ( 'cache_write', 1 );
		return $this->setValue ( $key, $value, $expire );
	}
	
	/**
	 * 删除缓存数据
	 *
	 * @param string $key 获取缓存数据的标识
	 * @return boolean
	 */
	public function delete($key) {
		return $this->deleteValue ( $key );
	}
	
	/**
	 * 清空缓存
	 */
	public function flush() {
		return $this->flushValues ();
	}
	
	/**
	 * 构造保存的数据
	 *
	 * @param string $value 保存缓存数据。
	 * @param string $type 缓存类型
	 * @param int $expires 缓存数据的过期时间,0表示永不过期
	 */
	protected function buildData($value, $expires) {
		if (! is_array ( $value ) && empty ( $value )) $value = array ();
		$data = array (self::DATA => $value,self::EXPIRE => $expires,self::STORETIME => time () );
		return serialize ( $data );
	}
	
	/**
	 * 格式化输出
	 *
	 * 将从缓存对象中获得的缓存源数据进行格式化输出.该源数据是一个格式良好的数组的序列化字符串,需要反序列化获得源数组.
	 * 如果没有数据,则返回false
	 * 如果含有数据,则返回该数据
	 *
	 * @param string $key 缓存的key值
	 * @param string $value 缓存的数据的序列化值
	 * @return mixed 返回保存的真实数据,如果没有数值则返回false
	 */
	protected function formatData($key, $value) {
		if (! $value) return false;
		$data = unserialize ( $value );
		return $this->hasChanged ( $key, $data ) ? false : $data [self::DATA];
	}
	
	/**
	 * 判断数据是否已经被更新
	 *
	 * 如果缓存中有数据,则检查缓存依赖是否已经变更,如果变更则删除缓存,并且返回true.
	 * 如果没有更新则返回false.
	 *
	 * @param string $key 缓存的key
	 * @param array $data 缓存中的数据
	 * @return boolean true表示缓存已变更,false表示缓存未变改
	 */
	protected function hasChanged($key, array $data) {
		if ($data [self::EXPIRE]) {
			$_over_time = $data [self::EXPIRE] + $data [self::STORETIME];
			if ($_over_time >= time ()) {
				return false;
			} else {
				$this->delete ( $key );
				return true;
			}
		} else
			return false;
	}
}